﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ttbit.IGL;
using ttbit.math;

namespace ttbit.core
{

    public class Texture : IDisposable
    {
        public int TexID
        {
            get;
            internal set;
        }
        public ushort Width
        {
            get;
            internal set;
        }
        public ushort Height
        {
            get;
            internal set;
        }
        public TextureType Format
        {
            get;
            internal set;
        }
        public bool gray
        {
            get
            {
                return Format == IGL.TextureType.R_HalfFloat || Format == IGL.TextureType.R_Float || Format == IGL.TextureType.R_U8;
            }
        }

        //贴图格式不会经常变，早点搞定
        public Texture(ushort width, ushort height, IGL.TextureType format, byte[]? initdata = null)
        {
            this.Format = format;
            this.Width = width;
            this.Height = height;
            TexID = Env.GL.CreateTexture();
            Env.GL.BindTexture2D(TexID);
            Env.GL.TexImage2D(0, format, width, height, initdata);
        }

        public virtual void UploadData(ushort width, ushort height, IGL.TextureType format, byte[] data)
        {
            if (width != this.Width || height != this.Height)
                throw new Exception("error size");
            if (format != this.Format)
                throw new Exception("error format");

            Env.GL.BindTexture2D(TexID);
            Env.GL.TexImage2D(0, format, width, height, data);
        }
        public virtual void UploadData<T>(ushort width, ushort height, IGL.TextureType format, T[] data) where T : struct
        {
            if (width != this.Width || height != this.Height)
                throw new Exception("error size");
            if (format != this.Format)
                throw new Exception("error format");

            Env.GL.BindTexture2D(TexID);
            Env.GL.TexImage2D(0, format, width, height, data);
        }
        public virtual void UploadSubData(ushort umin, ushort vmin, ushort width, ushort height, IGL.TextureType format, byte[] data)
        {
            if (format != this.Format)
                throw new Exception("error format");

            Env.GL.BindTexture2D(this.TexID);
            Env.GL.TexSubImage2D(0, format, umin, vmin, width, height, data);
        }
        public virtual void Dispose()
        {
            Env.GL.DeleteTexture(TexID);
        }
        public virtual void ReadPixel<T>(T[] buf) where T:struct
        {
            int tfb= Env.GL.CreateFrameBuffer();
            Env.GL.BindFrameBuffer(tfb);
            Env.GL.FrameBufferTexture2D(FrameBufferSlot.ColorAttachment0, TexID, 0);
            Env.GL.ReadPixels(0,0,Width,Height,Format, buf);
            Env.GL.BindFrameBuffer(0);
            Env.GL.DeleteFrameBuffer(tfb);
        }
        static public void PreMultiAlpha(byte[] data)
        {
            for (var i = 0; i < data.Length / 4; i++)
            {
                var r = data[i * 4 + 0] / 255.0f;
                var g = data[i * 4 + 1] / 255.0f;
                var b = data[i * 4 + 2] / 255.0f;
                var a = data[i * 4 + 3] / 255.0f;
                data[i * 4 + 0] = (byte)(r * a * 255.0f);
                data[i * 4 + 1] = (byte)(g * a * 255.0f);
                data[i * 4 + 2] = (byte)(b * a * 255.0f);
            }
        }
        static public void PreMultiAlpha(float[] data)
        {
            for (var i = 0; i < data.Length / 4; i++)
            {
                var r = data[i * 4 + 0];
                var g = data[i * 4 + 1];
                var b = data[i * 4 + 2];
                var a = data[i * 4 + 3];
                data[i * 4 + 0] = r * a;
                data[i * 4 + 1] = g * a;
                data[i * 4 + 2] = b * a;
            }
        }

    }

    public class RenderTexture : Texture
    {
        public int FBID
        {
            get;
            private set;
        }
        public RenderTexture(ushort width, ushort height, IGL.TextureType format) : base(width, height, format, null)
        {
            FBID = Env.GL.CreateFrameBuffer();
        }
        public override void UploadData(ushort width, ushort height, TextureType format, byte[] data)
        {
            if (data != null)
                throw new Exception("not support.");
            this.Width = width;
            this.Height = height;
            Env.GL.BindTexture2D(TexID);
            Env.GL.TexImage2D(0, format, width, height, data);

        }
        public override void UploadData<T>(ushort width, ushort height, TextureType format, T[] data)
        {
            if (data != null)
                throw new Exception("not support.");
            this.Width = width;
            this.Height = height;
            Env.GL.BindTexture2D(TexID);
            Env.GL.TexImage2D(0, format, width, height, data);
        }
        public override void UploadSubData(ushort umin, ushort vmin, ushort width, ushort height, IGL.TextureType format, byte[] data)
        {
            throw new Exception("not support.");
        }
        public override void ReadPixel<T>(T[] buf) where T : struct
        {
            Env.GL.BindFrameBuffer(FBID);
            Env.GL.FrameBufferTexture2D(FrameBufferSlot.ColorAttachment0, TexID, 0);
            Env.GL.ReadPixels(0, 0, Width, Height, Format, buf);
            Env.GL.BindFrameBuffer(0);
        }
        public override void Dispose()
        {
            base.Dispose();
            Env.GL.DeleteFrameBuffer(FBID);
        }
        public void Active()
        {
            Env.GL.BindFrameBuffer(FBID);
            Env.GL.FrameBufferTexture2D(IGL.FrameBufferSlot.ColorAttachment0, TexID, 0);
            Env.GL.Viewport(0, 0, Width, Height);

        }
    }
}
